﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace StyleMVVM.DependencyInjection
{
	/// <summary>
	/// This interface represents a generic way to configure a Type export
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public interface IExportConfiguration<T> : IBaseExportConfiguration
	{
		/// <summary>
		/// Export the type with the specified priority
		/// </summary>
		/// <param name="priority"></param>
		/// <returns></returns>
		IExportConfiguration<T> WithPriority(int priority);

		/// <summary>
		/// Export as a specific type (usually an interface)
		/// </summary>
		/// <typeparam name="TExportType"></typeparam>
		/// <returns></returns>
		IExportConfiguration<T> As<TExportType>();

		/// <summary>
		/// Export type in this Environment (ExportEnvironement is a flag so it can be or'd)
		/// </summary>
		/// <param name="environment"></param>
		/// <returns></returns>
		IExportConfiguration<T> InEnvironment(ExportEnvironment environment);

		/// <summary>
		/// Export the type under the specified name
		/// </summary>
		/// <param name="name">name to export under</param>
		/// <returns></returns>
		IExportConfiguration<T> AsName(string name);

		/// <summary>
		/// Share this export between all callers (can be garbage collected)
		/// </summary>
		/// <returns></returns>
		IExportConfiguration<T> AndShared();

		/// <summary>
		/// Share this export permanently 
		/// </summary>
		/// <returns></returns>
		IExportConfiguration<T> AndSharedPermenantly();

		/// <summary>
		/// Marks the export as a singleton for the lifetime of the container
		/// </summary>
		/// <returns></returns>
		IExportConfiguration<T> AndSingleton();
			
		/// <summary>
		/// Allows you to mark this export to be cached using an absolute expire time after creation
		/// </summary>
		/// <param name="timeSpan"></param>
		/// <returns></returns>
		IExportConfiguration<T> AndCachedWithAbsoluteExpiration(TimeSpan timeSpan);

		/// <summary>
		/// Allows you to mark this export to be cached using a sliding expiration time
		/// </summary>
		/// <param name="timeSpan"></param>
		/// <returns></returns>
		IExportConfiguration<T> AndCachedWithSlidingExpiration(TimeSpan timeSpan);

		/// <summary>
		/// Mark this export as a core export (created up container startup)
		/// </summary>
		/// <param name="startOrder"></param>
		/// <returns></returns>
		IExportConfiguration<T> CoreExport(CoreExportStartOrder startOrder);

		/// <summary>
		/// This method allows you to specify which constructor to use ( x => new MyTypeName("Specific","Constructor") )
		/// </summary>
		/// <param name="constructorExpression">constructor expression ( x => new MyTypeName("Specific","Constructor") )</param>
		/// <returns></returns>
		IExportConfiguration<T> ImportConstructor(Expression<Action> constructorExpression);

		/// <summary>
		/// Uses the first constructor found for import (this is only recommended when you have one constructor)
		/// </summary>
		/// <returns></returns>
		IExportConfiguration<T> ImportDefaultConstructor();
		
		/// <summary>
		/// Mark a specific property to be initialized (a new object constructed and set to property)
		/// </summary>
		/// <param name="property">property expression ( x => x.PropertyName )</param>
		/// <returns></returns>
		IExportConfiguration<T> InitializeProperty(
			Expression<Func<T, object>> property);

		/// <summary>
		/// Mark a specific property to be initialized (a new object constructed and set to property)
		/// </summary>
		/// <param name="property">property expression ( x => x.PropertyName )</param>
		/// <param name="initializeValue">initialize value</param>
		/// <returns></returns>
		IExportConfiguration<T> InitializeProperty<TProperty>(
			Expression<Func<T, TProperty>> property, TProperty initializeValue);

		/// <summary>
		/// Mark a property for Import (using dependency injection container)
		/// </summary>
		/// <param name="property">property expression (x => x.PropertyName) </param>
		/// <param name="isRequired">is the import required (object will not construct if true and cannot be found)</param>
		/// <param name="exportName">export name to use (when null it will use the property type to locate the export)</param>
		/// <returns></returns>
		IExportConfiguration<T> ImportProperty(
			Expression<Func<T, object>> property, bool isRequired = true, string exportName = null);

		/// <summary>
		/// Mark a property for Import and provide the export for it
		/// </summary>
		/// <param name="property"></param>
		/// <param name="exportActivator"></param>
		/// <param name="isRequired"></param>
		/// <returns></returns>
		IExportConfiguration<T> ImportProperty(Expression<Func<T, object>> property,
		                                       IExportActivator exportActivator,
		                                       bool isRequired = true);

		/// <summary>
		/// Mark a method to be called upon activation passing in an arguement that has be located using the IoC
		/// </summary>
		/// <param name="method"></param>
		/// <param name="isRequired"></param>
		/// <param name="exportName"></param>
		/// <returns></returns>
		IExportConfiguration<T> ImportMethod(
			Expression<Action<T>> method, bool isRequired = true, string exportName = null);

		/// <summary>
		/// Mark a particular Action() as the activation action
		/// </summary>
		/// <param name="activationMethod"></param>
		/// <returns></returns>
		IExportConfiguration<T> ActivationMethod(Expression<Action<T>> activationMethod);

		/// <summary>
		/// Specify a custom life cycle container for the export
		/// </summary>
		/// <param name="container"></param>
		/// <returns></returns>
		IExportConfiguration<T> LifeCycleContainer(ILifeCycleContainer container);

		/// <summary>
		/// Adds a condition to the export
		/// </summary>
		/// <param name="conditionDelegate"></param>
		IExportConfiguration<T> When(ExportConditionDelegate conditionDelegate);

		/// <summary>
		/// Adds a condition to the export
		/// </summary>
		/// <param name="conditionDelegate"></param>
		IExportConfiguration<T> Unless(ExportConditionDelegate conditionDelegate);

		/// <summary>
		/// Adds a condition to the export
		/// </summary>
		/// <param name="condition"></param>
		IExportConfiguration<T> AndCondition(IExportCondition condition);

		/// <summary>
		/// Applies a new WhenInjectedInto condition on the export, using the export only when injecting into the specified class
		/// </summary>
		/// <typeparam name="TInjected"></typeparam>
		/// <returns></returns>
		IExportConfiguration<T> WhenInjectedInto<TInjected>();

		/// <summary>
		/// Applies a WhenClassHas condition, using the export only if injecting into a class that is attributed with TAttr
		/// </summary>
		/// <typeparam name="TAttr"></typeparam>
		/// <returns></returns>
		IExportConfiguration<T> WhenClassHas<TAttr>();

		/// <summary>
		/// Applies a WhenMemberHas condition, using the export only if the Property or method or constructor is attribute with TAttr
		/// </summary>
		/// <typeparam name="TAttr"></typeparam>
		/// <returns></returns>
		IExportConfiguration<T> WhenMemberHas<TAttr>();

		/// <summary>
		/// Applies a WhenTargetHas condition, using the export only if the Property or Parameter is attributed with TAttr
		/// </summary>
		/// <typeparam name="TAttr"></typeparam>
		/// <returns></returns>
		IExportConfiguration<T> WhenTargetHas<TAttr>();

		/// <summary>
		/// Sets up all public writable properties on the type to be injected
		/// </summary>
		/// <param name="required">are the properties required</param>
		/// <returns></returns>
		IExportConfiguration<T> AutoWireProperties(bool required = false);

		/// <summary>
		/// Adds metadata to an export
		/// </summary>
		/// <param name="metadataName"></param>
		/// <param name="metadataValue"></param>
		/// <returns></returns>
		IExportConfiguration<T> WithMetadata(string metadataName, object metadataValue);

		/// <summary>
		/// You can provide a cleanup method to be called 
		/// </summary>
		/// <param name="disposalCleanupDelegate"></param>
		/// <returns></returns>
		IExportConfiguration<T> DisposalCleanupDelegate(BeforeDisposalCleanupDelegate disposalCleanupDelegate);

		/// <summary>
		/// Allows you to add custom activation logic to process before the object is returned.
		/// </summary>
		/// <param name="enrichWithDelegate"></param>
		/// <param name="priority">order in which to apply enrichments</param>
		/// <returns></returns>
		IExportConfiguration<T> EnrichWith(EnrichWithDelegate enrichWithDelegate, int priority = 0);
	}
}